package courier

import (
	"bufio"
	"bytes"
	"fmt"
	"gitee.com/dongmingchao/decent-ft/src/caretaker"
	resourcePool "gitee.com/dongmingchao/decent-ft/src/resource-pool"
	"gitee.com/dongmingchao/decent-ft/src/utils"
	"log"
	"net"
	"strings"
)

var passMsg chan *bufio.Reader
var passClosed bool

func handleListen(reader *bufio.Reader, addr *net.UDPAddr) []byte {
	ask, err := reader.ReadByte()
	var buf bytes.Buffer
	switch Op(ask) {
	case Connect:
		rAddr := utils.NewUDPAddrFromAddr(addr)
		srcAddr := utils.UDPAddr{}
		srcAddr.Read(reader)
		stash := resourcePool.GTree{}
		stash.Read(reader)
		caretaker.GlobalStash.Neighbors = append(caretaker.GlobalStash.Neighbors, resourcePool.GNeighbor{
			GTree:      stash,
			RemoteAddr: &srcAddr,
		})
		fmt.Println("source IP", srcAddr)
		fmt.Println("remote IP", rAddr)
		println("is Same IP: ", srcAddr.IP.Equal(rAddr.IP))
		if srcAddr.IP.Equal(rAddr.IP) {
			buf.WriteByte(byte(Done))
			caretaker.GlobalStash.Write(&buf)
		} else {
			buf.WriteByte(byte(TCP))
			utils.NewUDPAddrFromAddr(bridgeAddr).Write(&buf)
		}
	case AskIndex:
		buf.WriteByte(byte(Done))
		caretaker.GlobalStash.Write(&buf)
	case Get:
		hash := make([]byte, 20)
		_, err = reader.Read(hash)
		println("receive Get", hash)
		if err == nil {
			buf.WriteByte(byte(Done))
			mark := fmt.Sprintf("%x", hash)
			file := caretaker.ReadStashFileByMark(mark)
			file.WriteTo(&buf)
		}
	case Update:
		fmt.Println("pool recv Update", addr.String())

	case Pass:
		fmt.Println("pool recv Pass", addr.String())
		parsePass(reader, addr)
	case Search: // 上浮/平流搜索
		fmt.Println("pool recv Search", addr.String())
		_, word, originAddr, srcAddr, jumpCount, originNei := parseSearch(reader, addr)
		f := caretaker.GlobalStash.SearchFileByNameExactly(word)
		retI, otherNeighbors := getSrcNeighbor(srcAddr, []int{})
		if retI == nil {
			log.Fatalln("Last Source Neighbor Not Found!")
		}
		srcNei := &caretaker.GlobalStash.Neighbors[*retI]
		fmt.Println("found src neighbor", srcNei.RemoteAddr)
		var apped bytes.Buffer
		originAddr.Write(&apped)
		if f == nil {
			if srcNei.Bypass == "" {
				apped.WriteByte(jumpCount)
				searchPassJumps(reader, jumpCount, apped)
				outer := bufio.NewReader(&apped)
				BridgeSearchFileContent(word, outer, otherNeighbors)
			} else {
				apped.WriteByte(jumpCount + 1)
				fmt.Println("write before jump", srcNei.Bypass)
				apped.WriteString(srcNei.Bypass)
				apped.WriteByte(0)
				searchPassJumps(reader, jumpCount, apped)
				outer := bufio.NewReader(&apped)
				BridgeSearchFileContent(word, outer, otherNeighbors)
			}
		} else {
			fmt.Println("file found:", word)
			file := caretaker.ReadStashFile(*f)
			searchBack(originNei, srcNei, originAddr, jumpCount, reader, file)
		}
	}
	return buf.Bytes()
}

func parseSearch(reader *bufio.Reader, addr net.Addr) (utils.UDPAddr, string, utils.UDPAddr, utils.UDPAddr, byte, *resourcePool.GNeighbor) {
	word, err := reader.ReadString(0)
	word = strings.TrimRight(word, string(0))
	fmt.Println("parse search word: ", word)
	if err != nil {
		log.Fatalln("parseSearch Read word Error")
	}
	rAddr := *utils.NewUDPAddrFromAddr(addr)
	srcAddr := utils.UDPAddr{}
	srcAddr.Read(reader)
	originAddr := utils.UDPAddr{}
	originAddr.Read(reader)
	jumpCount, err := reader.ReadByte()
	if err != nil {
		log.Fatalln("parseSearch bypass read jump count err", err)
	}
	var originNei *resourcePool.GNeighbor
	fmt.Println("remote Addr", rAddr.String())
	fmt.Println("originAddr", originAddr.String())
	fmt.Println("srcAddr", srcAddr.String())
	fmt.Println("jump count", jumpCount)
	for i, neighbor := range caretaker.GlobalStash.Neighbors {
		if neighbor.RemoteAddr.Equal(originAddr) {
			originNei = &caretaker.GlobalStash.Neighbors[i]
		}
	}
	return rAddr, word, originAddr, srcAddr, jumpCount, originNei
}

func parsePass(reader *bufio.Reader, addr net.Addr) {
	ttl, err := reader.ReadByte()
	if err != nil {
		log.Fatalln("Bridge Pass Read TTL error")
	}
	if ttl == 0 {
		fmt.Println("pass msg is closed?", passClosed)
		if passClosed {
			return
		}
		passMsg <- reader
	} else {
		pAddr, err := reader.ReadString(0)
		if err != nil {
			log.Fatalln("bridge pass read bypass addr err", err)
		}
		pAddr = strings.TrimRight(pAddr, string(0))
		fmt.Println("pass to addr", pAddr, ttl)
		conn := bridges[pAddr]
		fmt.Println("Pass by bridge", conn.LocalAddr(), "->", conn.RemoteAddr())
		var sent bytes.Buffer
		sent.WriteByte(byte(Pass))
		sent.WriteByte(ttl - 1)
		fmt.Println("sent header", sent.Bytes())
		fmt.Println("Pass reader length", reader.Buffered())
		if reader.Buffered() > 0 {
			rest := make([]byte, reader.Buffered())
			n, _ := reader.Read(rest)
			fmt.Println("reader read length", n)
			//fmt.Println("reader content", rest)
			sent.Write(rest)
		}
		//fmt.Println("Pass content", sent.Bytes())
		n, err := sent.WriteTo(&conn)
		if err == nil {
			fmt.Println("Bridge Pass", addr, n)
		} else {
			log.Fatalln("Bridge Pass Error", addr)
		}
	}
}

func finalResponse(buf []byte) []byte {
	var fin bytes.Buffer
	// 写入2个字节的包长度
	fin.Write(utils.UInt16ToBytes(uint16(len(buf))))
	fin.Write(buf)
	return fin.Bytes()
}
